/**
 * 
 */
package denaris.core;

import java.util.Enumeration;
import java.util.Vector;

import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.Sprite;

import denaris.core.controller.AbstractController;
import denaris.core.controller.DegressiveVectorController;
import denaris.core.entity.AbstractEntity;
import denaris.core.entity.Explosion;
import denaris.core.entity.Player;
import denaris.factory.FrameFactory;
import denaris.factory.ImageFactory;
import denaris.midlet.GameScreen;

/**
 * <p>
 * Represents a map which scrolls through the users display. Maps are assigned
 * with three types of entity groups:
 * <ol>
 * <li>player group - the players ship, its satellite and so forth</li>
 * <li>friendly group - objects that do not harm anyone. These can be collected
 * by the player which provides certain upgrades</li>
 * <li>enemy group - all types of entities that must be shot by the player</li>
 * </ol>
 * </p>
 * <p>
 * 
 * @author stkl23
 * 
 */
public abstract class AbstractDenarisMap {
	public class CollisionParticleSystem extends ExhaustSystem {
		public CollisionParticleSystem() {
			super();
			setColors(new int[] {0xFFFFFF, 0xE2F1FE, 0xBBDFFD, 0x94CCFC, 0x6DBAFB, 0x46A8FA, 0x0781E9, 0x05569B, 0x032B4D, 0x391413, 0xB76505, 0xFAE500, 0xFA6400, 0x78210A});
		}
		
		protected void createEmitters() {
			addEmitter(new AbstractParticleEmitter(32, 0, 0) {
				protected AbstractParticle createNew() {
					Sprite s = new Sprite(transparent, 1, 1);
					// create a formal degressive vector controller with 0, 0 as
					// delta values.. the real deltas are set in the
					// initializeEmittedParticle() method
					DegressiveVectorController controller = new DegressiveVectorController(
							0, 0);
					controller.setSprite(s);
					return new ExhaustParticle(controller);
				}

				protected void initializeEmittedParticle(int x, int y, AbstractParticle particle) {
					super.initializeEmittedParticle(x, y, particle);
					DegressiveVectorController controller = (DegressiveVectorController) particle.getController();
					controller.setDeltaX(-GameScreen.nextPositiveInt(4));
					controller.setDeltaY(GameScreen.RANDOM.nextInt(10) - 5);
				}
			});
		}
	}

	private static final int[] SMALL_EXPLOSION_FRAMES = FrameFactory.createSequence(0, 6, 3);
	
	private AbstractEntity player;

	private Vector friends = new Vector();

	private Vector enemies = new Vector();

	private Starfield starfield;

	private CollisionParticleSystem collisionParticleSystem = new CollisionParticleSystem();

	public AbstractDenarisMap() {
	}

	/**
	 * Maps can use this method to load their data. This method is invoked by
	 * the GameScreen class!
	 */
	public abstract void initialize();

	/** Reset the map back to the last Nav-Point */
	public abstract void reset();
	
	/**
	 * Paints the Entity groups. IMPORTANT: classes that override
	 * AbstractDenarisMap MUST paint their content BEFORE calling super.paint()!
	 * 
	 * @param g
	 */
	public void paint(Graphics g) {
		// draw the children
		collisionParticleSystem.paint(g);

		paintGroup(enemies, g);
		paintGroup(friends, g);
		player.paint(g);
	}

	protected void paintGroup(Vector v, Graphics g) {
		Enumeration e = v.elements();
		while (e.hasMoreElements()) {
			AbstractEntity group = (AbstractEntity) e.nextElement();
			group.paint(g);
		}
	}

	public void addEnemy(AbstractEntity enemy) {
		enemies.addElement(enemy);
	}

	public void addFriend(AbstractEntity friend) {
		friends.addElement(friend);
	}

	public AbstractEntity getPlayerGroup() {
		return player;
	}

	public void setPlayerGroup(AbstractEntity playerGroup) {
		this.player = playerGroup;
	}

	protected void updateGroup(Vector v) {
		Enumeration e = v.elements();
		while (e.hasMoreElements()) {
			AbstractEntity group = (AbstractEntity) e.nextElement();
			group.update();
		}
	}

	public void update() {
		collisionParticleSystem.update();

		player.update();
		updateGroup(friends);
		updateGroup(enemies);
	}

	public void removeEnemy(AbstractEntity group) {
		removeGroup(enemies, group);
		// dispose the group
		group = null;
	}

	public void removeFriend(AbstractEntity group) {
		removeGroup(friends, group);
	}

	private void removeGroup(Vector v, AbstractEntity group) {
		if (v.contains(group)) {
			v.removeElement(group);
		}
	}

	public Vector getEnemies() {
		return enemies;
	}

	public Vector getFriends() {
		return friends;
	}

	public Starfield getStarfield() {
		return starfield;
	}

	public void setStarfield(Starfield starfield) {
		this.starfield = starfield;
	}

	public int getPlayerScore() {
		if (player instanceof Player) {
			return ((Player) player).getScore();
		} else {
			return -1;
		}
	}

	public void scrollVertical(Vector group, int dy) {
		for (int i = 0; i < group.size(); i++) {
			AbstractEntity entity = (AbstractEntity) group.elementAt(i);
			entity.scrollVertical(dy);
		}
	}

	public void addSmallExplosion(int x, int y) {
		Explosion e = new Explosion(this, x, y, 0, 4, false) {
			public AbstractController createController() {
				Sprite s = new Sprite(ImageFactory.getImage("/denaris/resource/sprite/explosion_small.png"), 12, 11);
				s.setFrameSequence(SMALL_EXPLOSION_FRAMES);
				AbstractController abstractController = super.createController();
				abstractController.setSprite(s);
				return abstractController;
			}
		};
		
		addFriend(e);
	}
	
	public void addCollisionParticles(int x, int y) {
		collisionParticleSystem.emitt(x, y);
	}

	public abstract boolean collidesWith(Sprite sprite);

	public abstract int getHeight();

	public abstract int getWidth();

	public abstract int getX();

	public abstract int getY();

	public abstract void move(int dx, int dy);

	public CollisionParticleSystem getCollisionParticleSystem() {
		return collisionParticleSystem;
	}
}